"""
Python包装器模块，用于调用C语言词法分析库
"""
import os
import sys
import ctypes
from ctypes import c_char_p, c_int, POINTER, Structure, c_void_p

# 定义TokenType枚举
class TokenType:
    # 基于修改后的C语言版本
    Num = 128
    Fun = 129
    Sys = 130
    Glo = 131
    Loc = 132
    Id = 133
    Char = 134
    Else = 135
    Enum = 136
    If = 137
    Int = 138
    Return = 139
    Sizeof = 140
    While = 141
    Assign = 142
    Cond = 143
    Lor = 144
    Lan = 145
    Or = 146
    Xor = 147
    And = 148
    Eq = 149
    Ne = 150
    Lt = 151
    Gt = 152
    Le = 153
    Ge = 154
    Shl = 155
    Shr = 156
    Add = 157
    Sub = 158
    Mul = 159
    Div = 160
    Mod = 161
    Inc = 162
    Dec = 163
    Brak = 164

# 定义Token结构体
class Token(Structure):
    _fields_ = [
        ("type", c_int),
        ("text", c_char_p),
        ("line", c_int),
        ("column", c_int),
        ("value", c_int),
        ("hash", c_int)
    ]

# 定义LexResult结构体
class LexResult(Structure):
    _fields_ = [
        ("tokens", POINTER(Token)),
        ("count", c_int),
        ("error", c_char_p),
        ("errors", c_void_p),  # 使用void*来表示指向错误数组的指针
        ("error_count", c_int)  # 错误数量
    ]

class PyLexer:
    """Python包装器，用于调用C语言词法分析库"""
    
    def __init__(self):
        """初始化词法分析器"""
        # 获取库文件路径
        try:
            lib_path = self.get_lib_path()
            
            # 直接加载库文件
            self.lib = ctypes.CDLL(lib_path)
        except Exception as e:
            raise RuntimeError(f"无法加载词法分析器库: {e}")
        
        # 设置函数签名
        self.lib.analyze_c_code.argtypes = [c_char_p]
        self.lib.analyze_c_code.restype = POINTER(LexResult)
        
        self.lib.init_lexer.argtypes = [c_char_p]
        self.lib.init_lexer.restype = None
        
        self.lib.next.argtypes = []
        self.lib.next.restype = None
        
        self.lib.match.argtypes = [c_int]
        self.lib.match.restype = None
        
        self.lib.free_lex_result.argtypes = [POINTER(LexResult)]
        self.lib.free_lex_result.restype = None
        
        self.lib.get_result_string.argtypes = [POINTER(LexResult)]
        self.lib.get_result_string.restype = c_void_p
        
        # 设置free_result_string函数签名
        self.lib.free_result_string.argtypes = [c_void_p]
        self.lib.free_result_string.restype = None
        
        # 添加get_error_string函数的绑定
        self.lib.get_error_string.argtypes = [POINTER(LexResult)]
        self.lib.get_error_string.restype = c_void_p
    
    # 获取库文件路径的辅助函数
    def get_lib_path(self):
        """获取词法分析器库文件的路径"""
        # 获取当前目录（lexer文件夹）
        current_dir = os.path.dirname(os.path.abspath(__file__))
        
        # 使用动态库
        lib_dir = os.path.join(os.path.dirname(current_dir), 'lib')
        lib_path = os.path.join(lib_dir, 'clexer2.dll')
        
        # 检查库文件是否存在，不存在则尝试在当前目录查找
        if not os.path.exists(lib_path):
            lib_path = os.path.join(current_dir, 'clexer2.dll')
            if not os.path.exists(lib_path):
                raise RuntimeError(f"找不到词法分析器动态库文件，请先编译")
            
        return lib_path
    
    
    def get_result_buffer(self, code):
        """
        调用C语言词法分析器分析代码并获取结果缓冲区
        
        Args:
            code: C源代码字符串
            
        Returns:
            指向LexResult结构体的指针
        """
        if not isinstance(code, bytes):
            code = code.encode('utf-8')
        
        # 调用C库分析代码
        result_ptr = self.lib.analyze_c_code(code)
        if not result_ptr:
            raise RuntimeError("分析失败，返回了空指针")
            
        return result_ptr
    
    def get_format_result(self, result_ptr):
        """
        从结果指针获取格式化的词法分析结果字符串
        
        Args:
            result_ptr: 指向LexResult结构体的指针
            
        Returns:
            格式化的结果字符串
        """
        # 获取格式化结果字符串
        c_str_ptr = self.lib.get_result_string(result_ptr)
        if not c_str_ptr:
            return ""
            
        try:
            # 转换为Python字符串
            formatted_str = ctypes.string_at(c_str_ptr).decode('utf-8', errors='replace')
        except Exception:
            try:
                # 如果UTF-8解码失败，尝试Latin-1
                formatted_str = ctypes.string_at(c_str_ptr).decode('latin-1')
            except:
                formatted_str = "无法解码结果字符串"
        finally:
            # 释放C字符串内存
            self.lib.free_result_string(c_str_ptr)
            
        return formatted_str
    
    def get_format_error(self, result_ptr):
        """
        从结果指针获取格式化的错误信息字符串
        
        Args:
            result_ptr: 指向LexResult结构体的指针
            
        Returns:
            格式化的错误信息字符串，如果没有错误则返回空字符串
        """
        # 检查是否有错误
        result = result_ptr.contents
        if not hasattr(result, 'error_count') or result.error_count <= 0:
            return ""
            
        # 获取错误字符串
        error_str_ptr = self.lib.get_error_string(result_ptr)
        if not error_str_ptr:
            return ""
            
        try:
            # 转换为Python字符串
            error_msg = ctypes.string_at(error_str_ptr).decode('utf-8', errors='replace')
        except Exception as e:
            error_msg = f"解码错误信息失败: {str(e)}"
        finally:
            # 释放C字符串内存
            self.lib.free_result_string(error_str_ptr)
            
        return error_msg
    
    def get_formatted_result_and_error(self, code):
        """
        分析C源代码并返回格式化的结果字符串和错误信息
        
        Args:
            code: C源代码字符串
            
        Returns:
            tuple: (格式化的结果字符串, 错误信息)
        """
        result_ptr = None
        formatted_str = ""
        error_msg = ""
        
        try:
            # 获取分析结果
            result_ptr = self.get_result_buffer(code)
            
            # 获取格式化结果
            formatted_str = self.get_format_result(result_ptr)
            
            # 获取错误信息
            error_msg = self.get_format_error(result_ptr)
            
        except Exception as e:
            import traceback
            error_details = traceback.format_exc()
            error_msg = f"处理过程出错: {str(e)}\n{error_details}"
            print(error_msg)  # 控制台输出错误
            return "", error_msg
        finally:
            # 确保释放结果指针
            if result_ptr:
                self.lib.free_lex_result(result_ptr)
                
        return formatted_str, error_msg

    # TokenType是C语言中的枚举类型，在C语言中定义，在Python中使用
    def _get_token_type_name(self, token_type):
        """获取token类型的名称"""
        names = {
            TokenType.Num: "NUMBER",
            TokenType.Fun: "FUNCTION",
            TokenType.Sys: "SYSTEM",
            TokenType.Glo: "GLOBAL",
            TokenType.Loc: "LOCAL",
            TokenType.Id: "IDENTIFIER",
            TokenType.Char: "CHAR",
            TokenType.Else: "ELSE",
            TokenType.Enum: "ENUM",
            TokenType.If: "IF",
            TokenType.Int: "INT",
            TokenType.Return: "RETURN",
            TokenType.Sizeof: "SIZEOF",
            TokenType.While: "WHILE",
            TokenType.Assign: "ASSIGN",
            TokenType.Cond: "COND",
            TokenType.Lor: "LOR",
            TokenType.Lan: "LAN",
            TokenType.Or: "OR",
            TokenType.Xor: "XOR",
            TokenType.And: "AND",
            TokenType.Eq: "EQ",
            TokenType.Ne: "NE",
            TokenType.Lt: "LT",
            TokenType.Gt: "GT",
            TokenType.Le: "LE",
            TokenType.Ge: "GE",
            TokenType.Shl: "SHL",
            TokenType.Shr: "SHR",
            TokenType.Add: "ADD",
            TokenType.Sub: "SUB",
            TokenType.Mul: "MUL",
            TokenType.Div: "DIV",
            TokenType.Mod: "MOD",
            TokenType.Inc: "INC",
            TokenType.Dec: "DEC",
            TokenType.Brak: "BRAK"
        }
        return names.get(token_type, "UNKNOWN")  # 如果找不到对应的类型，返回UNKNOWN

# 示例用法
if __name__ == "__main__":
    import sys
    
    try:
        # 检查命令行参数
        if len(sys.argv) != 2:
            print("用法: python py_lexer.py <c_source_file>")
            sys.exit(1)
            
        # 获取文件路径
        file_path = sys.argv[1]
        
        # 读取文件内容
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                code = f.read()
        except FileNotFoundError:
            print(f"错误：文件 '{file_path}' 不存在")
            sys.exit(1)
        except Exception as e:
            print(f"读取文件时发生错误: {e}")
            sys.exit(1)
            
        # 创建词法分析器实例
        lexer = PyLexer()
        
        # 获取分析结果
        result, error_result = lexer.get_formatted_result_and_error(code)
        
        # 打印词法分析结果
        print("\n词法分析结果:")
        print("=" * 50)
        print(result)
        
        # 如果有错误，打印错误信息
        if error_result:
            print("\n词法错误:")
            print("=" * 50)
            print(error_result)
            
    except Exception as e:
        print(f"错误: {e}")
        sys.exit(1) 